﻿// ****************************************************************************
// <copyright file="RelayCommandGeneric.cs" company="GalaSoft Laurent Bugnion">
// Copyright © GalaSoft Laurent Bugnion 2009-2013
// </copyright>
// ****************************************************************************
// <author>Laurent Bugnion</author>
// <email>laurent@galasoft.ch</email>
// <date>22.4.2009</date>
// <project>GalaSoft.MvvmLight</project>
// <web>http://www.galasoft.ch</web>
// <license>
// See license.txt in this project or http://www.galasoft.ch/license_MIT.txt
// </license>
// ****************************************************************************
// <credits>This class was developed by Josh Smith (http://joshsmithonwpf.wordpress.com) and
// slightly modified with his permission.</credits>
// ****************************************************************************
using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;
using TimersXP.Helpers;

namespace TimersXP.CommandBase
{
    /// <summary>
    /// A command whose sole purpose is to relay its functionality to other objects by invoking delegates. The default return value for the CanExecute method is <c>true</c>.
    /// This class does not allow you to accept command parameters in the Execute and CanExecute callback methods. Use the RelayCommandGeneric for passing parameters.
    /// </summary>
    /// ////[ClassInfo(typeof(RelayCommand),
    ////  VersionString = "4.0.11",
    ////  DateString = "201206191330",
    ////  Description = "A command whose sole purpose is to relay its functionality to other objects by invoking delegates.",
    ////  UrlContacts = "http://www.galasoft.ch/contact_en.html",
    ////  Email = "laurent@galasoft.ch")]
    public class RelayCommand : ICommand
    {
        #region Class Variables
        /// <summary>The execute WeakAction class variable</summary>
        private readonly WeakAction execute;
        /// <summary>The can execute WeakAction boolean class variable</summary>
        private readonly WeakFunction<bool> canExecute;
        #endregion Class Variables

        #region Constructors
        /// <summary>Initializes a new instance of the <see cref="RelayCommand"/> class that can always execute.</summary>
        /// <param name="execute">The execute logic.</param>
        /// <exception cref="ArgumentNullException">If the execute argument is null.</exception>
        public RelayCommand(Action execute) : this(execute, null) { }

        /// <summary>Initializes a new instance of the <see cref="RelayCommand"/> class.</summary>
        /// <param name="execute">The execute logic.</param>
        /// <param name="canExecute">The can execute status logic.</param>
        /// <exception cref="System.ArgumentNullException">execute</exception>
        public RelayCommand(Action execute, Func<bool> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            this.execute = new WeakAction(execute);
            if (canExecute != null)
                this.canExecute = new WeakFunction<bool>(canExecute);
        }
        #endregion Constructors

        #region Events
#if SILVERLIGHT
        /// <summary>Occurs when changes occur that affect whether or not the command should execute.</summary>
        public event EventHandler CanExecuteChanged;
#else
#if NETFX_CORE
        /// <summary>Occurs when changes occur that affect whether or not the command should execute.</summary>
        public event EventHandler CanExecuteChanged;
#else
#if XAMARIN
        /// <summary>Occurs when changes occur that affect whether or not the command should execute.</summary>
        public event EventHandler CanExecuteChanged;
#else
        /// <summary>Occurs when changes occur that affect whether or not the command should execute.</summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (this.canExecute != null)
                    CommandManager.RequerySuggested += value;
            }

            remove
            {
                if (this.canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }
#endif
#endif
#endif
        #endregion Events

        #region Public Functions
        /// <summary>Raises the <see cref="CanExecuteChanged"/> event.</summary>
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMemberAsStatic", Justification = "The this keyword is used in the Silverlight version")]
        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This cannot be an event")]
        public void RaiseCanExecuteChanged()
        {
#if SILVERLIGHT
            var handler = CanExecuteChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
#else
#if NETFX_CORE
            var handler = CanExecuteChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
#else
#if XAMARIN
            var handler = CanExecuteChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
#else
            CommandManager.InvalidateRequerySuggested();
#endif
#endif
#endif
        }

        /// <summary>Defines the method that determines whether the command can execute in its current state.</summary>
        /// <param name="parameter">This parameter will always be ignored.</param>
        /// <returns>true if this command can be executed; otherwise, false.</returns>
        public bool CanExecute(object parameter) { return this.canExecute == null || (this.canExecute.IsStatic || this.canExecute.IsAlive) && this.canExecute.Execute(); }

        #region Public Virtual Functions
        /// <summary>Defines the method to be called when the command is invoked.</summary>
        /// <param name="parameter">This parameter will always be ignored.</param>
        public virtual void Execute(object parameter)
        {
            if (CanExecute(parameter) && this.execute != null && (this.execute.IsStatic || this.execute.IsAlive))
                this.execute.Execute();
        }
        #endregion Public Virtual Functions
        #endregion Public Functions
    }
}
